using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Text.RegularExpressions;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization.TypeConverters
{
    /// <summary>
    /// Provides a type converter to convert expandable objects to and from various other representations.
    /// </summary>
    public class ColorTypeConverter : ExpandableObjectConverter
    {
        private static readonly char listSeparator = CultureInfo.CurrentCulture.TextInfo.ListSeparator[0];
        private static readonly Regex colorStringRegex = new Regex(@"^(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))" + listSeparator + @"){2}(((\d{1,2})|(1\d{2})|(2[0-4] \d)|(25[0-5]))){1}$", RegexOptions.Compiled);

        /// <summary>
        /// Returns whether this converter can convert the object to the specified type, using the specified context.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
        /// <param name="destinationType">A <see cref="T:System.Type"/> that represents the type you want to convert to.</param>
        /// <returns>
        /// true if this converter can perform the conversion; otherwise, false.
        /// </returns>
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(Color))
                return true;

            return base.CanConvertTo(context, destinationType);
        }

        /// <summary>
        /// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
        /// <param name="sourceType">A <see cref="T:System.Type"/> that represents the type you want to convert from.</param>
        /// <returns>
        /// true if this converter can perform the conversion; otherwise, false.
        /// </returns>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
                return true;

            return base.CanConvertFrom(context, sourceType);
        }


        /// <summary>
        /// Converts the given value object to the specified type, using the specified context and culture information.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
        /// <param name="culture">A <see cref="T:System.Globalization.CultureInfo"/>. If null is passed, the current culture is assumed.</param>
        /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
        /// <param name="destinationType">The <see cref="T:System.Type"/> to convert the <paramref name="value"/> parameter to.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the converted value.
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="destinationType"/> parameter is null. </exception>
        /// <exception cref="T:System.NotSupportedException">The conversion cannot be performed. </exception>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value,
                                         Type destinationType)
        {
            if ((destinationType == typeof(String)) && (value is Color))
            {
                var color = (Color)value;
                System.Drawing.Color rgbColor = color.ToColor();

                if (rgbColor.IsKnownColor || rgbColor.IsNamedColor)
                    return rgbColor.Name;

                KnownColor knownColor = rgbColor.ToKnownColor();
                if (knownColor.ToString() != "0")
                    return knownColor.ToString();

                try
                {
                    ColorConverter colorConverter = new ColorConverter();
                    object convertFrom = colorConverter.ConvertFrom(context, CultureInfo.CurrentCulture, String.Format(culture, "{0}{3}{1}{3}{2}", rgbColor.R, rgbColor.G, rgbColor.B, listSeparator));
                    rgbColor = (System.Drawing.Color)convertFrom;
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                }

                if (rgbColor.IsKnownColor || rgbColor.IsNamedColor)
                    return rgbColor.Name;

                return String.Format(culture, "{0}{3}{1}{3}{2}", rgbColor.R, rgbColor.G, rgbColor.B, listSeparator);
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }

        /// <summary>
        /// Converts the given object to the type of this converter, using the specified context and culture information.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that provides a format context.</param>
        /// <param name="culture">The <see cref="T:System.Globalization.CultureInfo"/> to use as the current culture.</param>
        /// <param name="value">The <see cref="T:System.Object"/> to convert.</param>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the converted value.
        /// </returns>
        /// <exception cref="T:System.NotSupportedException">The conversion cannot be performed. </exception>
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value is string)
            {
                var textValue = (string)value;
                if (!string.IsNullOrEmpty(textValue))
                {
                    Match match = colorStringRegex.Match(textValue);

                    if (!match.Success)
                    {
                        object convertFrom;

                        try
                        {
                            var colorConverter = new ColorConverter();
                            convertFrom = colorConverter.ConvertFrom(context, culture, textValue);
                        }
                        catch (Exception e)
                        {
                            throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "Invalid color. (use: R{0}G{0}B or color name)", listSeparator), e);
                        }

                        if (convertFrom is System.Drawing.Color)
                        {
                            return Color.FromColor((System.Drawing.Color)convertFrom);
                        }
                    }
                    else
                    {
                        string[] rgbs = textValue.Split(listSeparator);
                        return Color.FromColor(System.Drawing.Color.FromArgb(int.Parse(rgbs[0]), int.Parse(rgbs[1]), int.Parse(rgbs[2])));
                    }
                }
                return null;
            }
            return base.ConvertFrom(context, culture, value);
        }
    }
}